package crmdna.mail;

import static crmdna.common.AssertUtils.ensureNotNull;
import static crmdna.common.OfyService.ofy;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import com.microtripit.mandrillapp.lutung.model.MandrillApiError;
import com.microtripit.mandrillapp.lutung.view.MandrillMessageStatus;

public class Mail {

  public enum MailStatus {
    QUEUED, SENT, DEFERRAL, REJECTED_BY_MANDRILL, SOFT_BOUNCE, HARD_BOUNCE, OPENED, CLICKED, REPORTED_AS_SPAM, UNSUB
  }

  // send, deferral, hard_bounce, soft_bounce, open, click, spam, unsub, reject

  static Set<String> getAlreadySentEmailAddresses(String namespace, Long bulkEmailId) {

    if (bulkEmailId == null)
      return new HashSet<>();

    List<SentMailEntity> sentMailEntities =
        ofy(namespace).load().type(SentMailEntity.class).filter("bulkEmailId", bulkEmailId)
            .project("toEmail").list();

    Set<String> alreadySentEmailAddresses = new HashSet<>();
    for (SentMailEntity sentMailEntity : sentMailEntities) {
      alreadySentEmailAddresses.add(sentMailEntity.email);
    }

    return alreadySentEmailAddresses;
  }

  static Map<String, SentMailEntity> getSentMailEntities(String client, Set<String> mailIds) {

    // TODO: ensure valid client
    ensureNotNull(mailIds, "mailIds is null");

    Map<String, SentMailEntity> map = ofy(client).load().type(SentMailEntity.class).ids(mailIds);

    return map;
  }

  public static int sendMandrillEmail(String client, Set<String> emails,
      Map<String, Map<String, String>> emailVsMergeVar, Map<String, Long> emailVsMemberId,
      String subject, String htmlBody, String from, String subaccount, Set<String> tags,
      Long bulkEmailId, boolean doNotSendMultipleForSameAddress) throws MandrillApiError,
      IOException {

    // returns number of recipients mailed

    // TODO: ensure valid client
    // TODO: ensure valid bulk email id

    // TODO: allow this only for valid users (or privileged users)

    ensureNotNull(htmlBody, "htmlBody is null");
    ensureNotNull(from, "from is null");
    ensureNotNull(emails, "emails is null");
    ensureNotNull(subject, "subject is null");

    if (doNotSendMultipleForSameAddress == true) {
      ensureNotNull(bulkEmailId,
          "bulkEmailId not specified but doNotSendMultipleForSameAddress is true");

      Set<String> alreadySentEmailAddresses = getAlreadySentEmailAddresses(client, bulkEmailId);
      emails.removeAll(alreadySentEmailAddresses);
    }

    Logger logger = Logger.getLogger(Mail.class.getName());
    logger.info("emails.size() = " + emails.size());
    if (emails.isEmpty())
      return 0;

    Map<String, MandrillMessageStatus> map =
        Mandrill.send(emails, emailVsMergeVar, subject, htmlBody, from, subaccount);

    int numEmailsSent = map.size();

    List<SentMailEntity> sentMailEntities = new ArrayList<>(map.size());
    long currentTS = new Date().getTime();
    for (String email : map.keySet()) {

      SentMailEntity sentMailEntity = new SentMailEntity();

      MandrillMessageStatus mandrillMessageStatus = map.get(email);
      sentMailEntity.sentMailId = mandrillMessageStatus.getId();
      sentMailEntity.bulkEmailId = bulkEmailId;

      if ((emailVsMemberId != null) && emailVsMemberId.containsKey(email))
        sentMailEntity.memberId = emailVsMemberId.get(email);

      // sent in a-sync mode. status will be queued
      sentMailEntity.status = MailStatus.QUEUED;
      sentMailEntity.queuedTS = currentTS;
      sentMailEntity.statusTS = currentTS;

      sentMailEntity.tags = tags;
      sentMailEntity.email = email;

      sentMailEntities.add(sentMailEntity);
    }

    ofy(client).save().entities(sentMailEntities);

    if (tags == null)
      tags = new HashSet<>();

    if (bulkEmailId != null)
      tags.add("BULKEMAIL||" + bulkEmailId);

    Map<String, MailStatsProp> tagVsMailStatsProp = new HashMap<>(tags.size());
    for (String tag : tags) {
      MailStatsProp mailStatsProp = new MailStatsProp();
      mailStatsProp.tag = tag;
      mailStatsProp.numSendAttempted = numEmailsSent;

      tagVsMailStatsProp.put(tag, mailStatsProp);
    }

    MailStats.updateStats(client, tagVsMailStatsProp);

    return numEmailsSent;
  }
}
